﻿using System.ComponentModel.DataAnnotations.Schema;
using System.Text.RegularExpressions;
using Core.Domain.Contracts;
using Core.Domain.Enums;
using Core.Domain.Events;
using Core.Domain.ValueObjects;

namespace Core.Domain.Entities;

/// <summary>
/// 权限表
/// </summary>
[Table("Permissions")]
public class Permission : AuditableEntity, IAggregateRoot
{
    public string Code { get; private set; } = null!;
    public string Name { get; private set; } = null!;
    public string? Group { get; private set; }
    public Guid? ParentId { get; private set; }
    /// <summary>
    /// 权限作用域
    /// </summary>
    public PermissionScope? Scope { get; private set; }
    /// <summary>
    /// 溯源权限ID
    /// </summary>
    public Guid? SourcePermissionId { get; private set; }

    // 导航属性
    public virtual Permission? Parent { get; private set; }
    #region MenuMeta
    /// <summary>
    /// 菜单类型（0:菜单 1:iframe 2:外链 3:按钮）
    /// </summary>
    public int? MenuType { get; private set; } = 0;
    public string? Title { get; private set; }
    public string? RouteName { get; private set; } = null!;
    public string? Path { get; private set; }
    public string? Component { get; private set; }
    public int? Rank { get; private set; } = 99;
    public string? Redirect { get; private set; }
    public string? Icon { get; private set; }
    public string? ExtraIcon { get; private set; }
    public string? EnterTransition { get; private set; }
    public string? LeaveTransition { get; private set; }
    public string? ActivePath { get; private set; }
    public string? FrameSrc { get; private set; }
    public bool? FrameLoading { get; private set; } = true;
    public bool? KeepAlive { get; private set; } = false;
    public bool? HiddenTag { get; private set; } = false;
    public bool? FixedTag { get; private set; } = false;
    public bool? ShowLink { get; private set; } = true;
    public bool? ShowParent { get; private set; } = false;
    #endregion

    public virtual IReadOnlyCollection<RolePermission>? RolePermissions { get; private set; }

    // 1. 业务规则：编码必须符合规范
    private static readonly Regex CodePattern = new(@"^[A-Z][a-zA-Z0-9]*(?::[A-Z][a-zA-Z0-9]*)*$");

    // 2. 构造器保护不变式
    private Permission() { }
    private Permission(
        string code,
        string name,
        MenuMeta? meta = null,
        Guid? parentId = null,
        string? group = null)
    {
        Code = code;
        Name = name;
        Group = group;
        ParentId = parentId;
        Enabled = true;
        if (meta != null)
        {
            UpdateMeta(meta);
        }
        QueueDomainEvent(new PermissionCreate(this));
    }

    public static Permission Create(string code,
        string name,
        MenuMeta? meta = null,
        Guid? parentId = null,
        string? group = null)
    {
        if (!CodePattern.IsMatch(code))
            throw new BoilerplateException($"权限编码 {code} 不符合规范");

        return new Permission(code, name, meta, parentId, group);
    }

    public void UpdateMeta(MenuMeta newMeta)
    {
        MenuType = newMeta.MenuType;
        Title = newMeta.Title;
        Name = newMeta.Title;
        RouteName = newMeta.Name;
        Path = newMeta.Path;
        Component = newMeta.Component;
        Rank = newMeta.Rank;
        Redirect = newMeta.Redirect;
        Icon = newMeta.Icon;
        ExtraIcon = newMeta.ExtraIcon;
        EnterTransition = newMeta.EnterTransition;
        LeaveTransition = newMeta.LeaveTransition;
        ActivePath = newMeta.ActivePath;
        FrameSrc = newMeta.FrameSrc;
        FrameLoading = newMeta.FrameLoading;
        KeepAlive = newMeta.KeepAlive;
        HiddenTag = newMeta.HiddenTag;
        FixedTag = newMeta.FixedTag;
        ShowLink = newMeta.ShowLink;
        ShowParent = newMeta.ShowParent;
        Enabled = newMeta.ShowLink;
    }

    public void Rename(string newName)
    {
        if (string.IsNullOrWhiteSpace(newName))
            throw new BoilerplateException("权限名称不能为空");
        Name = newName;
    }

    public void ChangeParent(Guid? parentId)
    {
        ParentId = parentId;
    }

    public void ChangeScope(PermissionScope? scop = PermissionScope.Tenant)
    {
        if (Scope == scop)
            return;
        Scope = scop;
    }

    public void MoveToGroup(string? newGroup)
    {
        if (Group == newGroup)
            return;
        Group = newGroup;
        QueueDomainEvent(new PermissionMoved(Code, newGroup));
    }

    public void ChangeCode(string newCode)
    {
        Code = newCode;
        QueueDomainEvent(new PermissionCodeChanged(Code, newCode));
    }

    public void ChangeSourcePermission(Guid? sourcePermissionId)
    {
        SourcePermissionId = sourcePermissionId;
    }
}
